#include "context.h"
#include <stdexcept>
Context::Context(int logN, const std::vector<uint64_t> &qi, const std::vector<uint64_t> &pi, uint64_t t, double sigma,
    RingType ringType)
    : logN(logN), sigma(sigma), ringType(ringType), t(t)
{
    this->qi.resize(qi.size());
    this->pi.resize(pi.size());

    for (int i = 0; i < qi.size(); i++) {
        this->qi[i] = qi[i];
    }

    for (int i = 0; i < pi.size(); i++) {
        this->pi[i] = pi[i];
    }

    uint64_t N = 1ULL << logN;
    ringQ = new Ring(N, this->qi);
    ringP = new Ring(N, this->pi);

    // 创建一个执行密文乘法的模数QMul，当执行密文乘法时转换到QMul上。
    int nbQiMul = int(ceil(double(mpz_sizeinbase(ringQ->modulusBigint->get_mpz_t(), 2) + logN) / 59.0));

    ringQMul = new Ring(N, GenerateNTTPrimesP(59, 2 * N, nbQiMul));

    ringT = new Ring(N, std::vector<uint64_t>{ t });
}

Context::Context(const Context &other)
    : logN(other.logN), qi(other.qi), pi(other.pi), sigma(other.sigma), t(other.t), ringType(other.ringType)
{
    // 深拷贝指针成员
    this->ringQ = other.ringQ ? new Ring(other.ringQ->N, other.ringQ->Modulus) : nullptr;
    this->ringP = other.ringP ? new Ring(other.ringP->N, other.ringP->Modulus) : nullptr;
    this->ringQMul = other.ringQMul ? new Ring(other.ringQMul->N, other.ringQMul->Modulus) : nullptr;
    this->ringT = other.ringT ? new Ring(other.ringT->N, other.ringT->Modulus) : nullptr;
}

Context &Context::operator = (const Context &other)
{
    if (this == &other) {
        // 防止自我赋值
        return *this;
    }

    // 赋值普通成员变量
    this->logN = other.logN;
    this->qi = other.qi;
    this->pi = other.pi;
    this->sigma = other.sigma;
    this->t = other.t;
    this->ringType = other.ringType;

    // 深拷贝指针成员
    this->ringQ = other.ringQ ? new Ring(other.ringQ->N, other.ringQ->Modulus) : nullptr;
    this->ringP = other.ringP ? new Ring(other.ringP->N, other.ringP->Modulus) : nullptr;
    this->ringQMul = other.ringQMul ? new Ring(other.ringQMul->N, other.ringQMul->Modulus) : nullptr;
    this->ringT = other.ringT ? new Ring(other.ringT->N, other.ringT->Modulus) : nullptr;

    return *this;
}

int Context::QCount()
{
    return qi.size();
}

int Context::PCount()
{
    return pi.size();
}

int Context::MaxLevel()
{
    return QCount() - 1;
}

int Context::N()
{
    return 1 << logN;
}

uint64_t Context::T()
{
    return ringT->Modulus[0];
}

int Context::Alpha()
{
    return PCount();
}

int Context::Beta()
{
    if (Alpha() != 0) {
        return int(ceil(double(QCount()) / double(Alpha())));
    }
    return 1;
}

RingQP *Context::NewRingQP()
{
    return new RingQP(ringQ, ringP);
}

/*
 * @brief 根据旋转的下标k生成galois元素，为了后续生成galois密钥
 *
 * @param k 旋转下标，默认k为正对应左移旋转
 * @return uint64_t 对应的galois元素
 */
uint64_t Context::GaloisElementForColumnRotationBy(int k)
{
    return ModExp(GaloisGen, uint64_t(k & int(ringQ->NthRoot - 1)), ringQ->NthRoot);
}
/*
 * @brief 生成行位移对应的galois元素，行位移的galois元素是唯一的
 *
 * @return uint64_t galois元素
 */
uint64_t Context::GaloisElementForRowRotation()
{
    if (ringType == RingType::ConjugateInvariant) {
        throw std::runtime_error("Cannot generate GaloisElementForRowRotation if ringType is ConjugateInvariant");
    }
    return ringQ->NthRoot - 1;
}
/*
 * @brief galois元素求逆
 *
 * @param galEl galois元素
 * @return uint64_t 求逆的结果
 */
uint64_t Context::InverseGaloisElement(uint64_t galEl)
{
    return ModExp(galEl, ringQ->NthRoot - 1, ringQ->NthRoot);
}
/*
 * @brief 计算给定级别下与 `qi` 相关的溢出余量。
 *
 * 该函数基于 64 位无符号整数的最大值与 `qi` 在指定级别下的最大值之比，计算出溢出容忍度。
 * 它通过返回一个整数值表示最大可能的容忍度。
 *
 * @param level 级别参数，表示计算时的切片或区间。
 * @return 返回一个整数值，表示该级别下的溢出余量。
 */
int Context::QiOverflowMargin(int level)
{
    return int(std::pow(2.0, 64) / double(MaxSliceUint64(qi, level + 1)));
}
/*
 * @brief 计算给定级别下与 `pi` 相关的溢出余量。
 *
 * 该函数基于 64 位无符号整数的最大值与 `pi` 在指定级别下的最大值之比，计算出溢出容忍度。
 * 它通过返回一个整数值表示最大可能的容忍度。
 * @param level 级别参数，表示计算时的切片或区间。
 * @return 返回一个整数值，表示该级别下的溢出余量。
 */
int Context::PiOverflowMargin(int level)
{
    return int(std::pow(2.0, 64) / double(MaxSliceUint64(pi, level + 1)));
}

std::vector<uint64_t> Context::GaloisElementsForRowInnerSum()
{
    std::vector<uint64_t> galEls(logN);
    for (int64_t i = 0; i < logN - 1; i++) {
        galEls[i] = GaloisElementForColumnRotationBy(1 << i);
    }

    switch (ringType) {
        case RingType::Standard:
            galEls[logN - 1] = GaloisElementForRowRotation();
            break;
        case RingType::ConjugateInvariant:
            galEls[logN - 1] = GaloisElementForColumnRotationBy(1 << (logN - 1));
            break;
        default:
            throw std::runtime_error("invalid ring type");
    }

    return galEls;
}
